Add JiraAgent

The agent does periodical queries and emits
the events containing the updated issues in JSON format.

The agent also supports HTTP basic authentication.

Konstantin Nazarov 11 years ago
parent
commit
4fec08a6e0
1 changed files with 125 additions and 0 deletions
  1. 125 0
      app/models/agents/jira_agent.rb

+ 125 - 0
app/models/agents/jira_agent.rb

@@ -0,0 +1,125 @@
1
+#!/usr/bin/env ruby
2
+
3
+require 'cgi'
4
+require 'httparty'
5
+require 'date'
6
+
7
+module Agents
8
+  class JiraAgent < Agent
9
+    cannot_receive_events!
10
+
11
+    description <<-MD
12
+      The Jira Agent subscribes to Jira issue updates.
13
+
14
+      `jira_url` specidies the full URL of the jira installation, including https://
15
+      `jql` is an optional Jira Query Language-based filter to limit the flow of events. See [JQL Docs](https://confluence.atlassian.com/display/JIRA/Advanced+Searching) for details. 
16
+      `username` and `password` are optional, and may need to be specified if your Jira instance is read-protected
17
+
18
+      The agent does periodical queries and emits the events containing the updated issues in JSON format.
19
+      NOTE: upon the first execution, the agent will fetch everything available by the JQL query. So if it's not desirable, limit the `jql` query by date.
20
+    MD
21
+
22
+    event_description <<-MD
23
+      Events are the raw JSON generated by Jira REST API
24
+
25
+      {
26
+        "expand": "editmeta,renderedFields,transitions,changelog,operations",
27
+        "id": "80127",
28
+        "self": "https://jira.atlassian.com/rest/api/2/issue/80127",
29
+        "key": "BAM-3512",
30
+        "fields": {
31
+          ...
32
+        }
33
+      }
34
+    MD
35
+
36
+    default_schedule "every_10m"
37
+
38
+    def default_options
39
+      {
40
+        'username'  => '',
41
+        'password' => '',
42
+        'jira_url' => 'https://jira.atlassian.com',
43
+        'jql' => '' 
44
+      }
45
+    end
46
+
47
+    def validate_options
48
+        errors.add(:base, "you need to specify your jira URL") unless options['jira_url'].present?
49
+    end
50
+
51
+    def working?
52
+      (events_count.present? && events_count > 0)
53
+    end
54
+
55
+    def check
56
+      last_run = nil
57
+
58
+      current_run = Time.now.utc.iso8601
59
+      last_run = Time.parse(memory[:last_run]) if memory[:last_run]
60
+      begin
61
+        issues = get_issues(last_run)
62
+
63
+        issues.each do |issue|
64
+          create_event :payload => issue
65
+        end
66
+
67
+        memory[:last_run] = current_run
68
+      rescue Exception => e
69
+        log(e.message)
70
+      end
71
+    end
72
+
73
+  private
74
+    def request_url(jql, start_at)
75
+      "#{options[:jira_url]}/rest/api/2/search?jql=#{CGI::escape(jql)}&fields=*all&startAt=#{start_at}"
76
+    end
77
+
78
+    def request_options
79
+      ropts = {:headers => {"User-Agent" => "Huginn (https://github.com/cantino/huginn)"}}
80
+
81
+      if !options[:username].empty?  then
82
+        ropts = ropts.merge({:basic_auth => {:username =>options[:username], :password=>options[:password]}})
83
+      end
84
+
85
+      ropts
86
+    end
87
+
88
+
89
+    def get_issues(since)
90
+      startAt = 0
91
+      issues = []
92
+
93
+      jql = ""
94
+
95
+      if !options[:jql].empty? && since then 
96
+        jql = "(#{options[:jql]}) and updated >= '#{since.strftime('%Y-%m-%d %H:%M')}'"
97
+      else
98
+        jql = options[:jql] if !options[:jql].empty?
99
+        jql = "updatedd >= '#{since.strftime('%Y-%m-%d %H:%M')}'" if since
100
+      end
101
+
102
+
103
+      loop do
104
+        response = HTTParty.get(request_url(jql, startAt), request_options)
105
+
106
+        if response.code == 400 then
107
+          raise RuntimeError.new("Jira error: #{response['errorMessages']}") 
108
+        elsif response.code == 403 then
109
+          raise RuntimeError.new("Authentication failed: Forbidden (403)")
110
+        elsif response.code != 200 then
111
+          raise RuntimeError.new("Request failed: #{response}")
112
+        end
113
+
114
+        issues += response['issues']
115
+        startAt += response['issues'].length
116
+ 
117
+        break if startAt >= response['total']
118
+      end
119
+
120
+      issues
121
+    end
122
+
123
+  end
124
+end
125
+